﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Ndo.Maths.MOP
{
    public abstract class BenchMark<Vector>
        where Vector : class, IList<double>, ICloneable
    {
        public abstract Vector F(Vector x);
        public virtual Vector[] YOptimums(int c)
        {
            return null;
        }
        public virtual Vector[] XOptimums(int c)
        {
            return null;
        }
        public int XDimension { get { return LowerBound.Count; } }
        private int yDimension = - 1;
        public int YDimension
        {
            get
            {
                if (yDimension == -1)
                {
                    yDimension = F(LowerBound).Count;
                }
                return yDimension;
            }
        }
        public Vector LowerBound;
        public Vector UpperBound;
        public bool Check(Vector x)
        {
            if(LowerBound.Count != x.Count)
                return false;
            for(int i=0; i<x.Count; i++)
            {
                if(x[i] < LowerBound[i] || UpperBound[i]<x[i])
                    return false;
            }
            return true;
        }
    }

    public class BenchMarkMetric<Vector>
        where Vector : class, IList<double>, ICloneable
    {
        public readonly BenchMark<Vector> BM;
        public readonly Vector[] YOptimums;
        public BenchMarkMetric(BenchMark<Vector> bm, int c)
        {
            BM = bm;
            if (c <= 0)
                throw new Exception();
            YOptimums = bm.YOptimums(c);
            if (YOptimums == null)
            {
                YOptimums = bm.XOptimums(c);
                if (YOptimums != null)
                {
                    for (int i = 0; i < YOptimums.Length; i++)
                    {
                        YOptimums[i] = bm.F(YOptimums[i]);
                    }
                }
            }
        }
        public double[] ConvergenceDistances(IList<Vector> set)
        {
            int n = set.Count;
            double[] ret = new double[n];
            for (int i = 0; i < n; i++)
            {
                double min = double.PositiveInfinity;
                Vector v = set[i];
                for (int j = 0; j < YOptimums.Length; j++)
                {
                    min = Math.Min(Measure.EuclideanDistance(v, YOptimums[j]), min);
                }
                ret[i] = min;
            }
            return ret;
        }
        private static int SimpleCompare(Vector x, Vector y)
        {
            for (int i = 0, c; i < x.Count; i++)
            {
                if ((c = x[i].CompareTo(y[i])) != 0)
                    return c;
            }
            return 0;
        }
        public double[] Deltas(ICollection<Vector> set)
        {
            if (set.Count == 0)
                return new double[0];
            int n = set.Count;
            Vector[] values = new Vector[n];
            double[] ret = new double[n - 1];
            set.CopyTo(values, 0);
            Array.Sort(values, SimpleCompare);
            for (int i = 1; i < n; i++)
            {
                ret[i - 1] = Measure.EuclideanDistance(values[i - 1], values[i]);
            }
            return ret;
        }
        public double Entropy(ICollection<Vector> set)
        {
            if (set.Count == 0)
                return double.NaN;
            int m = BM.YDimension;
            int l = (int) Math.Ceiling(Math.Pow(set.Count, 1.0/m));
            //todo;
            return double.NaN;
        }
    }

}
